package ua.com.stormlabs.tiny.licenseserver.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import ua.com.stormlabs.tiny.licenseserver.beans.User;
import ua.com.stormlabs.tiny.licenseserver.storage.*;
import ua.com.stormlabs.tiny.licenseserver.storage.entities.LicenseEntity;
import ua.com.stormlabs.tiny.licenseserver.storage.entities.UserEntity;
import ua.com.stormlabs.tiny.licenseserver.utils.ArgumentGuard;

import java.util.LinkedList;
import java.util.List;

/**
 * @author Maxim Bondarenko
 * @version 1.0 12/24/11
 */

@Service
public class UsersServiceImpl implements UsersService {
    private static final Logger log = LoggerFactory.getLogger(UsersServiceImpl.class);

    @Autowired
    LicensesRepository licensesRepository;

    @Autowired
    UsersRepository usersRepository;

    @Override
    public RequestResult<User> listUsers(int pageNumber, int pageSize) {
        return listUsers(new PageRequest(pageNumber, pageSize));
    }

    @Override
    public RequestResult<User> listUsers(Pageable fetchLimits) {
        Page<UserEntity> userEntities = usersRepository.findAll(fetchLimits);
        List<User> users = new LinkedList<>();

        for (UserEntity userEntity : userEntities) {
            users.add(BeanConverter.convert(userEntity));
        }

        long usersCount = usersRepository.count();

        return new RequestResult<>(users, usersCount);
    }

    @Override
    public List<User> listUsers(int licenseId) {
        LicenseEntity licenseEntity = new LicenseEntity();
        licenseEntity.setId(licenseId);
        List<UserEntity> users = usersRepository.findByLicense(licenseEntity);
        List<User> res = new LinkedList<>();
        for (UserEntity userEntity : users) {
            res.add(BeanConverter.convert(userEntity));
        }
        return res;
    }

    @Override
    public User createUser(User user) throws DuplicateUserException, LicenseNotFoundException {
        ArgumentGuard.checkNotNull(user, "user");

        String name = user.getUserName();

        UserEntity duplicate = usersRepository.findByUserName(name);
        if (duplicate != null) {
            String msg = "User with name [" + name + "] already exists";
            log.error(msg);
            throw new DuplicateUserException(msg);
        }

        LicenseEntity licenseEntity = licensesRepository.findOne(user.getLicenseType());
        if (licenseEntity == null) {
            String msg = "License with id [" + user.getLicenseType() + "] doesn't exists";
            log.error(msg);
            throw new LicenseNotFoundException(msg);
        }

        UserEntity userEntity = BeanConverter.convert(user, licenseEntity);
        userEntity = usersRepository.saveAndFlush(userEntity);
        return BeanConverter.convert(userEntity);
    }

    @Override
    public User getUserByName(String userName) throws UserNotFoundException {
        ArgumentGuard.checkNotNull(userName, "userName");
        ArgumentGuard.checkNotBlank(userName, "userName");

        UserEntity userEntity = usersRepository.findByUserName(userName);
        if (userEntity == null) {
            String msg = "User with name [" + userName + "] doesn't exists";
            log.error(msg);
            throw new UserNotFoundException(msg);
        }

        return BeanConverter.convert(userEntity);
    }

    @Override
    public User getUserById(int userId) throws UserNotFoundException {
        ArgumentGuard.checkNotNegativeInteger(userId, "userId");

        UserEntity userEntity = ensureUserExists(userId);

        return BeanConverter.convert(userEntity);
    }

    @Override
    public void updateUser(User user) throws UserNotFoundException, LicenseNotFoundException {
        ArgumentGuard.checkNotNull(user, "user");

        UserEntity userEntity = ensureUserExists(user.getId());

        userEntity.setUserName(user.getUserName());
        userEntity.setCreateDate(user.getCreateDate());
        userEntity.setExpireDate(user.getExpireDate());
        userEntity.setLicenseCode(user.getLicenseCode());
        userEntity.setMac(user.getMac());
        userEntity.setMacChecking(user.getMacChecking());

        LicenseEntity licenseEntity = licensesRepository.findOne(user.getLicenseType());
        if (licenseEntity == null) {
            String msg = "License with id [" + user.getLicenseType() + "] doesn't exists";
            log.error(msg);
            throw new LicenseNotFoundException(msg);
        }
        userEntity.setLicense(licenseEntity);
    }

    @Override
    public void deleteUserById(int userId) throws UserNotFoundException {
        ArgumentGuard.checkNotNegativeInteger(userId, "userId");

        UserEntity userEntity = usersRepository.findOne(userId);

        if (userEntity == null) {
            String msg = "User with id [" + userId + "] doesn't exist";
            log.error(msg);
            throw new UserNotFoundException(msg);
        }
        ensureUserExists(userId);

        usersRepository.delete(userId);
    }

    private UserEntity ensureUserExists(int userId) throws UserNotFoundException {
        UserEntity userEntity = usersRepository.findOne(userId);

        if (userEntity == null) {
            String msg = "User with id [" + userId + "] doesn't exist";
            log.error(msg);
            throw new UserNotFoundException(msg);
        }

        return userEntity;
    }

    @Override
    public void resetUserMac(int userId) throws UserNotFoundException {
        ArgumentGuard.checkNotNegativeInteger(userId, "userId");

        ensureUserExists(userId);

        UserEntity userEntity = usersRepository.findOne(userId);
        userEntity.setMac(null);
        usersRepository.saveAndFlush(userEntity);
    }

    @Override
    public void updateUserMac(int userId, String mac) throws UserNotFoundException {
        ArgumentGuard.checkNotNegativeInteger(userId, "userId");
        ArgumentGuard.checkNotNull(mac, "mac");
        ArgumentGuard.checkNotBlank(mac, "mac");

        ensureUserExists(userId);

        UserEntity userEntity = usersRepository.findOne(userId);
        userEntity.setMac(mac);
        usersRepository.saveAndFlush(userEntity);
    }
}
